#include "./ramfs.h"
#include "dfs.h"
#include "dfs_fs.h"
#include "dfs_file.h"
#include "loger.h"
#include "mem_sal.h"


/* 文件系统-ramfs,适用于ram内存构建的文件系统 */


#if CONFIG_MEM_ENABLE


/* static var */

#ifdef TAG
#undef TAG
#endif
#define TAG "RAMFS"

#include "../mem/tlsf/tlsf.h"

static tlsf_t tlsf_obj = NULL;
static size_t tlsf_mem_used = 0;
static size_t tlsf_mem_total = 0;


/* static func */

static void tlsf_work_cb(void *ptr, size_t size, int used, void *user)
{
    if (used)
    {
        tlsf_mem_used += size;
    }
    tlsf_mem_total += size;
}

static void tlsf_get_info(uint32_t *total, uint32_t *used, uint32_t *max_used)
{
    tlsf_mem_used = 0;
    tlsf_mem_total = 0;

    tlsf_walk_pool(tlsf_get_pool(tlsf_obj), tlsf_work_cb, 0);

    if (total) *total = tlsf_mem_total;
    if (used) *used = tlsf_mem_used;
    if (max_used) *max_used = tlsf_mem_used;
}

static int ramfs_mount(_dfs_fs_obj *fs, unsigned long rwflag, const void *data)
{
    if (data == NULL) return -EIO;

    fs->data = (_dfs_ramfs_obj*)data;
    
    return 0;
}

static int ramfs_unmount(_dfs_fs_obj *fs)
{
    fs->data = NULL;

    return 0;
}

static int ramfs_statfs(_dfs_fs_obj *fs, struct statfs *buf)
{
    _dfs_ramfs_obj *obj;
    uint32_t total, used;

    obj = (_dfs_ramfs_obj *)fs->data;
    if (obj == NULL || buf == NULL)
    {
        LOG_E(TAG, "obj or buf is null");   
    }

    buf->f_bsize = 512;
    tlsf_get_info(&total, &used, NULL);
    buf->f_blocks = total/ 512;
    buf->f_bfree  = (total-used) / 512;

    return 0;
}

static int ramfs_ioctl(_dfs_fd_obj *file, int cmd, void *args)
{
    return -EIO;
}

static _ramfs_dirent_t *ramfs_lookup(_dfs_ramfs_obj *ramfs, const char *path, size_t *size)
{
    const char *subpath;
    _ramfs_dirent_t *dirent;

    subpath = path;
    while (*subpath == '/' && *subpath) subpath ++;

    if (! *subpath) /* is root directory */
    {
        *size = 0;

        return &(ramfs->root);
    }

    for (dirent = list_entry(ramfs->root.list.next, _ramfs_dirent_t, list);
         dirent != &(ramfs->root);
         dirent = list_entry(dirent->list.next, _ramfs_dirent_t, list))
    {
        if (strcmp(dirent->name, subpath) == 0)
        {
            *size = dirent->size;
            return dirent;
        }
    }

    /* not found */
    return NULL;
}

static int ramfs_read(_dfs_fd_obj *file, void *buf, size_t count)
{
    size_t length;
    _ramfs_dirent_t *dirent;

    dirent = (_ramfs_dirent_t *)file->data;
    if (dirent == NULL)
    {
        LOG_E(TAG, "ramfs_read dirent NULL");
        return 0;
    }

    if (count < (file->size-file->pos)) length = count;
    else length = file->size-file->pos;

    if (length > 0)
    {
        memcpy(buf, &(dirent->data[file->pos]), length);
    }

    /* update file current position */
    file->pos += length;

    return length;
}

static int ramfs_write(_dfs_fd_obj *fd, const void *buf, size_t count)
{
    _ramfs_dirent_t *dirent;
    _dfs_ramfs_obj *ramfs;

    dirent = (_ramfs_dirent_t *)fd->data;
    if (dirent == NULL)
    {
        LOG_E(TAG, "ramfs_write dirent NULL");
        return -1;
    }

    ramfs = dirent->fs;
    if (ramfs == NULL)
    {
        LOG_E(TAG, "ramfs_write ramfs NULL");
        return -ENOMEM;
    }

    if (count + fd->pos > fd->size)
    {
        uint8_t *ptr = NULL;
        // ptr = rt_memheap_realloc(&(ramfs->memheap), dirent->data, fd->pos + count);
        ptr = tlsf_realloc(tlsf_obj, dirent->data, fd->pos + count);
        if (ptr == NULL)
        {
            LOG_E(TAG, "ramfs_write prt realloc fail");
            return 0;
        }

        /* update dirent and file size */
        dirent->data = ptr;
        dirent->size = fd->pos + count;
        fd->size = dirent->size;
    }

    if (count > 0)
    {
        memcpy(dirent->data + fd->pos, buf, count);
    }

    /* update file current position */
    fd->pos += count;

    return count;
}

static int ramfs_lseek(_dfs_fd_obj *file, off_t offset)
{
    if (offset <= (off_t)file->size)
    {
        file->pos = offset;
        return file->pos;
    }
    return -5;
}

static int ramfs_close(_dfs_fd_obj *file)
{
    file->data = NULL;
    return 0;
}

static int ramfs_open(_dfs_fd_obj *file)
{
    size_t size;
    _dfs_ramfs_obj *ramfs;
    _ramfs_dirent_t *dirent;
    _dfs_fs_obj *fs;

    fs = (_dfs_fs_obj *)file->data;

    ramfs = (_dfs_ramfs_obj *)fs->data;
    if (ramfs == NULL)
    {
        LOG_A(TAG, "ramfs_open ramfs NULL");
    }

    if (file->flags & O_DIRECTORY)
    {
        if (file->flags & O_CREAT)
        {
            return -ENOSPC;
        }

        /* open directory */
        dirent = ramfs_lookup(ramfs, file->path, &size);
        if (dirent == NULL)
            return -ENOENT;
        if (dirent == &(ramfs->root)) /* it's root directory */
        {
            if (!(file->flags & O_DIRECTORY))
            {
                return -ENOENT;
            }
        }
    }
    else
    {
        dirent = ramfs_lookup(ramfs, file->path, &size);
        if (dirent == &(ramfs->root)) /* it's root directory */
        {
            return -ENOENT;
        }

        if (dirent == NULL)
        {
            if (file->flags & O_CREAT || file->flags & O_WRONLY)
            {
                char *name_ptr;

                /* create a file entry */
                // dirent = (_ramfs_dirent_t *) rt_memheap_alloc(&(ramfs->memheap), sizeof(_ramfs_dirent_t));
                dirent = tlsf_malloc(tlsf_obj, sizeof(_ramfs_dirent_t));
                if (dirent == NULL)
                {
                    return -ENOMEM;
                }

                /* remove '/' separator */
                name_ptr = file->path;
                while (*name_ptr == '/' && *name_ptr)
                    name_ptr ++;
                strncpy(dirent->name, name_ptr, RAMFS_NAME_MAX);

                list_init(&(dirent->list));
                dirent->data = NULL;
                dirent->size = 0;
                dirent->fs = ramfs;

                /* add to the root directory */
                list_insert_after(&(ramfs->root.list), &(dirent->list));
            }
            else
                return -ENOENT;
        }

        /* Creates a new file.
         * If the file is existing, it is truncated and overwritten.
         */
        if (file->flags & O_TRUNC)
        {
            dirent->size = 0;
            if (dirent->data != NULL)
            {
                // rt_memheap_free(dirent->data);
                tlsf_free(tlsf_obj, dirent->data);
                dirent->data = NULL;
            }
        }
    }

    file->data = dirent;
    file->size = dirent->size;
    if (file->flags & O_APPEND)
        file->pos = file->size;
    else
        file->pos = 0;

    return 0;
}

static int ramfs_stat(_dfs_fs_obj *fs, const char *path, struct stat *st)
{
    size_t size;
    _ramfs_dirent_t *dirent;
    _dfs_ramfs_obj *ramfs;

    ramfs = (_dfs_ramfs_obj *)fs->data;
    dirent = ramfs_lookup(ramfs, path, &size);

    if (dirent == NULL) return -ENOENT;

    // st->st_dev = 0;
    st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH |
                  S_IWUSR | S_IWGRP | S_IWOTH;

    st->st_size = dirent->size;
    st->st_mtime = 0;

    return 0;
}

static int ramfs_getdents(_dfs_fd_obj *file, struct dirent *dirp, uint32_t count)
{
    size_t index, end;
    struct dirent *d;
    _ramfs_dirent_t *dirent;
    _dfs_ramfs_obj *ramfs;

    dirent = (_ramfs_dirent_t *)file->data;

    ramfs  = dirent->fs;
    if (ramfs == NULL)
    {
        LOG_A(TAG, "ramfs_getdents ram NULL");
    }

    if (dirent != &(ramfs->root)) return -EINVAL;

    /* make integer count */
    count = (count / sizeof(struct dirent));
    if (count == 0) return -EINVAL;

    end = file->pos + count;
    index = 0;
    count = 0;
    for (dirent = list_entry(dirent->list.next, _ramfs_dirent_t, list);
         dirent != &(ramfs->root) && index < end;
         dirent = list_entry(dirent->list.next, _ramfs_dirent_t, list))
    {
        if (index >= (size_t)file->pos)
        {
            d = dirp + count;
            d->d_type = DT_REG;
            d->d_namlen = RAMFS_NAME_MAX;
            d->d_reclen = (uint16_t)sizeof(struct dirent);
            strncpy(d->d_name, dirent->name, RAMFS_NAME_MAX);

            count += 1;
            file->pos += 1;
        }
        index += 1;
    }

    return count * sizeof(struct dirent);
}

static int ramfs_unlink(_dfs_fs_obj *fs, const char *path)
{
    size_t size;
    _dfs_ramfs_obj *ramfs;
    _ramfs_dirent_t *dirent;

    ramfs = (_dfs_ramfs_obj *)fs->data;
    if (ramfs == NULL)
    {
        LOG_A(TAG, "ramfs_unlink ram NULL");
    }

    dirent = ramfs_lookup(ramfs, path, &size);
    if (dirent == NULL) return -ENOENT;

    list_remove(&(dirent->list));
    if (dirent->data)
    {
        tlsf_free(tlsf_obj, dirent->data);
    }
    tlsf_free(tlsf_obj, dirent);

    return 0;
}

static int ramfs_rename(_dfs_fs_obj *fs, const char *oldpath, const char *newpath)
{
    _ramfs_dirent_t *dirent;
    _dfs_ramfs_obj *ramfs;
    size_t size;

    ramfs = (_dfs_ramfs_obj *)fs->data;
    if (ramfs == NULL)
    {
        LOG_A(TAG, "ramfs_rename ramfs NULL");
    }

    dirent = ramfs_lookup(ramfs, newpath, &size);
    if (dirent != NULL) return -EEXIST;

    dirent = ramfs_lookup(ramfs, oldpath, &size);
    if (dirent == NULL) return -ENOENT;

    strncpy(dirent->name, newpath, RAMFS_NAME_MAX);

    return 0;
}


/* export func */


static const _dfs_file_opt ramfs_file_opt = {

    .open = ramfs_open,
    .close = ramfs_close,
    .ioctl = ramfs_ioctl,
    .read = ramfs_read,
    .write = ramfs_write,
    .flush = NULL,
    .lseek = ramfs_lseek,
    .getdents = ramfs_getdents,
};

static const _dfs_fs_opt ramfs_fs_opt = 
{
    .name = "ramfs",
    .flags = DFS_FS_FLAG_DEFAULT,

    .file_opt = &ramfs_file_opt,

    .mount = ramfs_mount,
    .unmount = ramfs_unmount,
    .mkfs = NULL,
    .statfs = ramfs_statfs,

    .unlink = ramfs_unlink,
    .stat = ramfs_stat,
    .rename = ramfs_rename,
};


int dfs_filesystem_ramfs_init(void)
{

    return 0;
}

_dfs_ramfs_obj *dfs_filesystem_ramfs_create(uint8_t *pool, size_t size)
{
    _dfs_ramfs_obj *obj = NULL;
    uint8_t *pdata;
    // int ret;

    if (pool == NULL)
    {
        LOG_E(TAG, "ramfs pool NULL");
        return NULL;
    }

    if (size <= (2*sizeof(_dfs_ramfs_obj)))
    {
        LOG_E(TAG, "ramfs size fail");
        return NULL;
    }

    obj = (_dfs_ramfs_obj*)pool;
    pdata = (uint8_t*)(obj + 1);
    size = size - sizeof(_dfs_ramfs_obj);
    size = ((size) & ~((4) - 1));   // 4字节对齐

    
    tlsf_obj = tlsf_create_with_pool(pdata, size);
    if (tlsf_obj == NULL)
    {
        LOG_E(TAG, "ramfs memheap create fail");
        return NULL;
    }

    obj->magic = RAMFS_MAGIC;

    /* 初始化根目录 */
    memset(&(obj->root), 0x00, sizeof(obj->root));
    list_init(&(obj->root.list));
    obj->root.size = 0;
    strcpy(obj->root.name, ".");
    obj->root.fs = obj;

    return obj;
}


#endif // CONFIG_MEM_ENABLE





